package com.android.stockme.backend;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import android.util.Log;

import com.android.stockme.backend.configuration.IConfigurationChangedListener;
import com.android.stockme.backend.configuration.IConfigurationManager;
import com.android.stockme.backend.configuration.PersistentConfigurationManager;
import com.android.stockme.backend.fetcher.DataFetcherFactory;
import com.android.stockme.backend.fetcher.IDataFetcher;
import com.android.stockme.backend.fetcher.INewsItem;
import com.android.stockme.backend.news.INewsUpdatesCenter;
import com.android.stockme.backend.news.NewsUpdatesCenterImpl;
import com.android.stockme.backend.portfolio.IStockData;
import com.android.stockme.backend.portfolio.IStockProtfolio;
import com.android.stockme.backend.portfolio.StockPortfolio;
import com.android.stockme.backend.util.Tupple;

public class BackendController implements IBackendController {

	public void run() {
		// TODO Auto-generated method stub
		
	}

	private static IBackendController INSTANCE = null;
	
	/**
	 * @uml.property   name="stockProtfolio"
	 * @uml.associationEnd   multiplicity="(1 1)" inverse="backendController:com.android.stockme.backend.portfolio.IStockProtfolio"
	 */
	private IStockProtfolio stockProtfolio = null;

	/**
	 * @uml.property   name="configurationManager"
	 * @uml.associationEnd   multiplicity="(1 1)" inverse="backendController:com.android.stockme.backend.configuration.IConfigurationManager"
	 */
	private IConfigurationManager configurationManager = null;

	/**
	 * @uml.property   name="dataFetcher"
	 * @uml.associationEnd   multiplicity="(1 1)" inverse="backendController:com.android.stockme.backend.fetcher.IDataFetcher"
	 */
	private IDataFetcher dataFetcher = null;

	/**
	 * @uml.property   name="messageQueue"
	 * @uml.associationEnd   multiplicity="(1 1)" inverse="backendController:com.android.stockme.backend.IBackendMessageQueue"
	 */
	private IBackendMessageQueue messageQueue = null;

//	private RefreshTask newsRefreshTask = null;
//	private RefreshTask portfolioRefreshTask = null;

	private Timer newsRefreshTask = null;
	private Timer portfolioRefreshTask = null;

	/** 
	 * @uml.property name="newsUpdatesCenter"
	 * @uml.associationEnd readOnly="true" multiplicity="(1 1)" inverse="backendController:com.android.stockme.backend.news.INewsUpdatesCenter"
	 */
	private INewsUpdatesCenter newsUpdatesCenter = null;

	private Map<EBackendEvents, Collection<ITickListener>> event2listeners = null;

	public static IBackendController getBackendController() {
		return INSTANCE;
	}
	
	/**
	 */
	protected BackendController(IBackendMessageQueue messageQueue){
		
		DataFetcherFactory factory = new DataFetcherFactory();

		this.initListenersMap();
		this.messageQueue = messageQueue;
		this.configurationManager = new PersistentConfigurationManager();
		this.dataFetcher = factory.createFetcher(this.configurationManager);
		this.stockProtfolio = new StockPortfolio(this.configurationManager);
		this.newsUpdatesCenter = new NewsUpdatesCenterImpl(this.configurationManager);
//		this.newsRefreshTask = new RefreshTask(new IASyncComputation() {
//			
//			private List<INewsItem> items = null;
//			
//			public void compute() {
//				Log.d("StockMe", "News Update Task start fetching news updates");
//				this.items = dataFetcher.fetchNewsUpdates(newsUpdatesCenter.getTimestamp());
//				Log.d("StockMe", "News Update Task finished fetching news updates");
//			}
//			public void run() {
//				Log.d("StockMe", "News Update Task updating DB");
//				newsUpdatesCenter.updateNewsCenter(this.items);
//				this.items = null;
//				postNewsTickEvent(null);
//			}
//		}, this.messageQueue, this.configurationManager.getNewsTickFrequency(), this.configurationManager.getNewsTickFrequency(), "News Update Task");
//		this.portfolioRefreshTask = new RefreshTask(new IASyncComputation() {
//			
//			private Map<String,Tupple<Float,Float>> items = null;
//			
//			public void compute() {
//				Log.d("StockMe", "Portfolio Update Task start fetching stocks data");
//				this.items = dataFetcher.fetchStocksData();
//				Log.d("StockMe", "Portfolio Update Task finished fetching stocks data");
//			}
//			public void run() {
//				Log.d("StockMe", "Portfolio Update Task updating DB");
//				stockProtfolio.setZerous();
//				stockProtfolio.updatePortfolio(this.items);
//				this.items = null;
//				postStockTickEvent(getAlertSymbols());
//			}
//		}, this.messageQueue, this.configurationManager.getNewsTickFrequency(), this.configurationManager.getNewsTickFrequency(), "Portfolio Update Task");

		this.newsRefreshTask = new Timer(this.messageQueue, "News Update Task", new IASyncComputation() {
			private List<INewsItem> items = null;
			
			public void compute() {
				Log.d("StockMe", "News Update Task start fetching news updates");
				this.items = dataFetcher.fetchNewsUpdates();
				Log.d("StockMe", "News Update Task finished fetching news updates");
			}
			public void run() {
				Log.d("StockMe", "News Update Task updating DB");
				newsUpdatesCenter.updateNewsCenter(this.items);
				this.items = null;
				postNewsTickEvent(null);
				newsRefreshTask.startScheduling();
			}			
		}, this.configurationManager.getNewsTickFrequency());
		
		this.portfolioRefreshTask = new Timer(this.messageQueue, "Portfolio Update Task", new IASyncComputation() {
			private Map<String,Tupple<Float,Float>> items = null;
			
			public void compute() {
				Log.d("StockMe", "Portfolio Update Task start fetching stocks data");
				this.items = dataFetcher.fetchStocksData();
				Log.d("StockMe", "Portfolio Update Task finished fetching stocks data");
			}
			public void run() {
				Log.d("StockMe", "Portfolio Update Task updating DB");
				stockProtfolio.setZerous();
				stockProtfolio.updatePortfolio(this.items);
				this.items = null;
				postStockTickEvent(getAlertSymbols());
				portfolioRefreshTask.startScheduling();
			}
		}, this.configurationManager.getStockTickFrequency());
		
		this.configurationManager.registerNewsTickFrequencyChangedListener(this.newsRefreshTask);
		this.configurationManager.registerStockTickFrequencyChangedListener(this.portfolioRefreshTask);
		
		this.configurationManager.registerFinanceWebsiteChangedListener(new IConfigurationChangedListener() {
			public void notifyConfigurationChanged(Object arg) {
				DataFetcherFactory factory = new DataFetcherFactory();
				
				dataFetcher = factory.createFetcher(configurationManager);
			}			
		});
		
		this.newsRefreshTask.start();
		this.portfolioRefreshTask.start();
	}

	public IConfigurationManager getConfigurationManager() {

		return this.configurationManager;
		
	}//getConfigurationManager

	public IDataFetcher getDataFetcher() {

		return this.dataFetcher;
		
	}//getDataFetcher

	
	public IStockProtfolio getStockPortfolio() {

		return this.stockProtfolio;

	}//getStockPortfolio

	/** 
	 * Getter of the property <tt>newsUpdatesCenter</tt>
	 * @return  Returns the newsUpdatesCenter.
	 * @uml.property  name="newsUpdatesCenter"
	 */
	public INewsUpdatesCenter getNewsUpdatesCenter() {
		
		return this.newsUpdatesCenter;
		
	}//getNewsUpdatesCenter

	public void startUpdating() {
//		this.newsRefreshTask.startRefresh();
//		this.portfolioRefreshTask.startRefresh();
		this.newsRefreshTask.startScheduling();
		this.portfolioRefreshTask.startScheduling();
	}

	public void stopUpdating() {
//		this.newsRefreshTask.stopRefresh();
//		this.portfolioRefreshTask.stopRefresh();		
		this.newsRefreshTask.stopScheduling();
		this.portfolioRefreshTask.stopScheduling();
	}
	
	public void registerNewsTickListener(ITickListener listener) {
		this.event2listeners.get(EBackendEvents.NEWS_TICK_EVENT).add(listener);
	}

	public void registerStockTickListener(ITickListener listener) {
		this.event2listeners.get(EBackendEvents.STOCK_TICK_EVENT).add(listener);		
	}
	
	public void unregisterNewsTickListener(ITickListener listener) {
		this.event2listeners.get(EBackendEvents.NEWS_TICK_EVENT).remove(listener);		
	}

	public void unregisterStockTickListener(ITickListener listener) {
		this.event2listeners.get(EBackendEvents.STOCK_TICK_EVENT).remove(listener);				
	}

	public List<Tupple<String, Float>> fetchStockHistory(String symbol) {
		return this.dataFetcher.fetchStockHistory(symbol);
	}
	
	private void initListenersMap() {
		this.event2listeners = new HashMap<EBackendEvents, Collection<ITickListener>>();
		for (EBackendEvents event : EBackendEvents.values())
			this.event2listeners.put(event, new HashSet<ITickListener>());
	}

	protected void postNewsTickEvent(Object arg) {
		for (ITickListener listener : this.event2listeners.get(EBackendEvents.NEWS_TICK_EVENT))
			listener.notifyTick(arg);
	}
	
	protected void postStockTickEvent(Object arg) {
		for (ITickListener listener : this.event2listeners.get(EBackendEvents.STOCK_TICK_EVENT))
			listener.notifyTick(arg);
	}

	private enum EBackendEvents
	{
		NEWS_TICK_EVENT,
		STOCK_TICK_EVENT
	}
	
	private Collection<String> getAlertSymbols() {
		Collection<String> alertSymbols = new HashSet<String>();
		
		for (String s : this.configurationManager.getStockSymbols()) {			
			boolean isHigh = this.configurationManager.isHighBoundConfigured(s);
			boolean isLow = this.configurationManager.isLowBoundConfigured(s);
			float high = this.configurationManager.getHighBound(s);
			float low = this.configurationManager.getLowBound(s);
			IStockData stockData = this.stockProtfolio.getStockData(s);
			
			if (stockData != null && isHigh && stockData.getRelativeChange() > high)
				alertSymbols.add(s);
			else if (stockData != null && isLow && stockData.getRelativeChange() < low)
					alertSymbols.add(s);
		}
		
		return alertSymbols;
	}
	
	public static class BackendControllerBuilder {
		
		private IBackendMessageQueue messageQueue = null;
		
		public BackendControllerBuilder(IBackendMessageQueue messageQueue) {
			this.messageQueue = messageQueue;
		}
		
		public IBackendController build() {
			if (INSTANCE == null)
				INSTANCE = new BackendController(this.messageQueue);			
			return INSTANCE;
		}
	}
}
